﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
namespace PSPROOFUtils
{
    public class ProofDataSetItem
    {
        /// <summary>
        /// Get the name of this dataset.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Get/Set the proof connection
        /// </summary>
        internal ROOTNET.Interface.NTProof ProofConnection { get; set; }

        /// <summary>
        /// True if this exists only locally and not on the server yet.
        /// </summary>
        internal bool IsLocal { get; set; }

        /// <summary>
        /// Create a new dataset with only the name info valid.
        /// </summary>
        /// <param name="name"></param>
        public ProofDataSetItem(string name, long nFiles, long nCorruptFiles, long nStagedFiles, long totalSize)
        {
            Name = name;
            NumberFiles = nFiles;
            NumberCorruptFiles = nCorruptFiles;
            NumberStagedFiles = nStagedFiles;
            TotalSizeBytes = totalSize;

            //
            // Other initalization.
            //

            InformationComplete = false;
            IsLocal = false;
        }

        /// <summary>
        /// Is the information we are storing complete or not?
        /// </summary>
        internal bool InformationComplete { get; private set; }

        /// <summary>
        /// A list of all files we know about.
        /// </summary>
        private List<ROOTNET.Interface.NTFileInfo> _files;

        /// <summary>
        /// Save the full data info for this dataset.
        /// </summary>
        /// <param name="files"></param>
        internal void SetFullData(System.Collections.Generic.IEnumerable<ROOTNET.Interface.NTFileInfo> files)
        {
            if (_files == null)
            {
                _files = new List<ROOTNET.Interface.NTFileInfo>();
            }
            else
            {
                _files.Clear();
            }

            _files.AddRange(files.Select(f => f.Clone() as ROOTNET.Interface.NTFileInfo));
            InformationComplete = true;
        }

        /// <summary>
        /// Enumerate through the list of files.
        /// </summary>
        /// <returns></returns>
        internal IEnumerable<ROOTNET.Interface.NTFileInfo> GetFileInfoEnumerator()
        {
            return _files;
        }

        /// <summary>
        /// Total number of files in the dataset
        /// </summary>
        public long NumberFiles { get; private set; }

        /// <summary>
        /// Total number of corrupt files in the dataset
        /// </summary>
        public long NumberCorruptFiles { get; private set; }

        /// <summary>
        /// Total number of staged files in the dataset.
        /// </summary>
        public long NumberStagedFiles { get; private set; }

        /// <summary>
        /// Total size in bytes of the dataset.
        /// </summary>
        public long TotalSizeBytes { get; private set; }

        /// <summary>
        /// Add an item to the internal list. We can add a number of things:
        ///  - String - this is just a single file name
        ///  - Array of String - an array of filenames
        /// </summary>
        /// <param name="newItemValue"></param>
        internal void AddItem(object newItemValue)
        {
            IsLocal = true;
            if (newItemValue.GetType().IsArray)
            {
                AddItems(newItemValue as object[]);
            }
            else
            {
                if (_files == null)
                    _files = new List<ROOTNET.Interface.NTFileInfo>();

                if (newItemValue is string)
                {
                    var f = new ROOTNET.NTFileInfo(newItemValue as string);
                    f.SetNativePointerOwner(false); // This will be owned by the list now!
                    _files.Add(f);
                    NumberFiles++;
                }
                else if (newItemValue is PSObject)
                {
                    AddItem((newItemValue as PSObject).BaseObject);
                }
            }
        }

        /// <summary>
        /// We have an array of items. Add them!
        /// </summary>
        /// <param name="itemList"></param>
        private void AddItems(object[] itemList)
        {
            foreach (var o in itemList)
            {
                AddItem(o);
            }
        }

        /// <summary>
        /// We should create this dataset on the server with all the files we know about!
        /// </summary>
        internal void UpdateServer(bool verify = false)
        {
            //
            // Quick cheks to see what we are going to be able to do (or not do).
            //

            if (_files == null || _files.Count == 0)
                throw new ArgumentException(string.Format("Unable to register dataset '{0}' on server until some files are attached to it using the set-content cmdlet", Name));

            if (ProofConnection == null)
                throw new InvalidOperationException("Unable to create the dataset because the proof connection is null!");

            //
            // Create a file collection
            //

            var fc = new ROOTNET.NTFileCollection(Name, Name);
            foreach (var f in _files)
            {
                fc.Add(f.Clone() as ROOTNET.Interface.NTFileInfo);
            }

            //
            // And register the dataset.
            //

            string opt = "U";
            if (verify)
                opt += "VS";

            var r = ProofConnection.RegisterDataSet(Name, fc, opt);
            if (!r)
                throw new InvalidOperationException(string.Format("Failed to create dataset '{0}' on the PROOF server.", Name));

            //
            // We are now check-pointed with the server. Mark ourselves as so...
            //

            IsLocal = false;
        }

        /// <summary>
        /// Remove a dataset from the server. No files are deleted when this happens.
        /// </summary>
        internal void RemoveFromServer()
        {
            if (ProofConnection == null)
                throw new InvalidOperationException("Dataset proof connection is null! Can't remove dataset");

            ProofConnection.RemoveDataSet(Name);
        }

        /// <summary>
        /// Clear the file list.
        /// </summary>
        internal void ClearAllFiles()
        {
            if (_files != null)
                _files.Clear();
        }
    }
}
